// Copyright (C) 2004-2005  Daniel Grunwald
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// For more information read the file "LICENSE.txt".

using System;

namespace Grunwald.NOscar
{
	/// <summary>
	/// This class works like the default .NET bitconverter, but converts to network byte order.
	/// </summary>
	public sealed class NetworkBitConverter
	{
		private NetworkBitConverter() {}
		
		static readonly bool active = BitConverter.IsLittleEndian;
		
		#region GetBytes
		static byte[] ToNetworkByteOrder(byte[] data)
		{
			if (!active)
				return data;
			int len = data.Length / 2;
			int j = data.Length;
			byte tmp;
			for (int i = 0; i < len; ++i) {
				tmp = data[i];
				data[i] = data[--j];
				data[j] = tmp;
			}
			return data;
		}
		
		/// <summary>Gets the bytes of the Int16 value.</summary>
		public static byte[] GetBytes(short number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		
		/// <summary>Gets the bytes of the Int32 value.</summary>
		public static byte[] GetBytes(int number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		
		/// <summary>Gets the bytes of the Int64 value.</summary>
		public static byte[] GetBytes(long number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		
		/// <summary>Gets the bytes of the UInt16 value.</summary>
		[CLSCompliant(false)]
		public static byte[] GetBytes(ushort number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		
		/// <summary>Gets the bytes of the UInt32 value.</summary>
		[CLSCompliant(false)]
		public static byte[] GetBytes(uint number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		
		/// <summary>Gets the bytes of the UInt64 value.</summary>
		[CLSCompliant(false)]
		public static byte[] GetBytes(ulong number)
		{
			return ToNetworkByteOrder(BitConverter.GetBytes(number));
		}
		#endregion
		
		#region PutBytes
		static void PutBytes(byte[] buffer, int offset, byte[] host)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (offset < 0)
				throw new ArgumentOutOfRangeException("offset", offset, "Negative offset not allowed.");
			if (offset + host.Length > buffer.Length)
				throw new ArgumentOutOfRangeException("offset", offset, "Start index is greater than array length minus " + host.Length + ".");
			if (active) {
				int pos = offset + host.Length;
				for (int i = 0; i < host.Length; ++i) {
					buffer[--pos] = host[i];
				}
			} else {
				host.CopyTo(buffer, offset);
			}
		}
		
		/// <summary>Puts the bytes of the Int16 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 2.</exception>
		public static void PutBytes(byte[] buffer, int offset, short number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		
		/// <summary>Puts the bytes of the Int32 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 4.</exception>
		public static void PutBytes(byte[] buffer, int offset, int number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		
		/// <summary>Puts the bytes of the Int64 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 8.</exception>
		public static void PutBytes(byte[] buffer, int offset, long number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		
		/// <summary>Puts the bytes of the UInt16 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 2.</exception>
		[CLSCompliant(false)]
		public static void PutBytes(byte[] buffer, int offset, ushort number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		
		/// <summary>Puts the bytes of the UInt32 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 4.</exception>
		[CLSCompliant(false)]
		public static void PutBytes(byte[] buffer, int offset, uint number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		
		/// <summary>Puts the bytes of the UInt64 number into the buffer.</summary>
		/// <exception cref="ArgumentNullException">buffer is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">offset is less than zero
		/// or greater than the length of buffer minus 8.</exception>
		[CLSCompliant(false)]
		public static void PutBytes(byte[] buffer, int offset, ulong number)
		{
			PutBytes(buffer, offset, BitConverter.GetBytes(number));
		}
		#endregion
		
		#region ToNumber
		static byte[] FromNetworkByteOrder(byte[] value, int startIndex, int count)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (startIndex < 0)
				throw new ArgumentOutOfRangeException("startIndex", startIndex, "Negative start index not allowed.");
			if (startIndex + count > value.Length)
				throw new ArgumentOutOfRangeException("startIndex", startIndex, "Start index is greater than array length minus " + count + ".");
			byte[] b = new byte[count];
			int pos = startIndex + count;
			for (int i = 0; i < b.Length; ++i) {
				b[i] = value[--pos];
			}
			return b;
		}
		
		/// <summary>Gets the Int16 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 2.</exception>
		public static short ToInt16(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToInt16(FromNetworkByteOrder(value, startIndex, 2), 0);
			else
				return BitConverter.ToInt16(value, startIndex);
		}
		
		/// <summary>Gets the Int32 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 4.</exception>
		public static int ToInt32(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToInt32(FromNetworkByteOrder(value, startIndex, 4), 0);
			else
				return BitConverter.ToInt32(value, startIndex);
		}
		
		/// <summary>Gets the Int64 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 8.</exception>
		public static long ToInt64(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToInt64(FromNetworkByteOrder(value, startIndex, 8), 0);
			else
				return BitConverter.ToInt64(value, startIndex);
		}
		
		/// <summary>Gets the UInt16 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 2.</exception>
		[CLSCompliant(false)]
		public static ushort ToUInt16(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToUInt16(FromNetworkByteOrder(value, startIndex, 2), 0);
			else
				return BitConverter.ToUInt16(value, startIndex);
		}
		
		/// <summary>Gets the UInt32 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 4.</exception>
		[CLSCompliant(false)]
		public static uint ToUInt32(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToUInt32(FromNetworkByteOrder(value, startIndex, 4), 0);
			else
				return BitConverter.ToUInt32(value, startIndex);
		}
		
		/// <summary>Gets the UInt64 value of the bytes.</summary>
		/// <exception cref="ArgumentNullException">value is null.</exception>
		/// <exception cref="ArgumentOutOfRangeException">startIndex is less than zero
		/// or greater than the length of value minus 8.</exception>
		[CLSCompliant(false)]
		public static ulong ToUInt64(byte[] value, int startIndex)
		{
			if (active)
				return BitConverter.ToUInt64(FromNetworkByteOrder(value, startIndex, 8), 0);
			else
				return BitConverter.ToUInt64(value, startIndex);
		}
		#endregion
	}
}
